package heap

import (
	"jvm/classfile"
	"strings"
)

type Class struct {
	accessFlags    uint16
	name           string
	sourceFile     string
	superClassName string
	interfaceNames []string
	constantPool   *ConstantPool
	fields         []*Field
	methods        []*Method
	loader         *ClassLoader
	superClass     *Class
	interfaces     []*Class
	/**
	vm加载的字节码，也就是.class文件，被加载到方法区里面，叫Kclass，是一个C++对象，含有类的信息、虚方法表等
	JVM在加载了字节码后，在堆里创建的Class对象，这个对象和方法区的Kclass相互指向，也就是说我们可以通过Kclass找到这个对象
	我们new 一个对象，对象头里面会有一个指针，指向方法区的Kclass
	new Object().getClass()流程， 对象头里面的指针–>方法区kClass–>堆Class对象
	反射也是拿到堆里的Class对象
	所有我们比较两个对象类型是否相等，实际上就是比较两个Class对象是否一样
	*/
	jClass            *Object
	instanceSlotCount uint
	staticVars        Slots
	initStarted       bool
}

func newClass(cf *classfile.ClassFile) *Class {
	class := &Class{}
	class.accessFlags = cf.AccessFlags()
	class.name = cf.ClassName()
	class.sourceFile = getSourceFile(cf)
	class.superClassName = cf.SuperClassName()
	class.interfaceNames = cf.InterfaceNames()
	class.constantPool = newConstantPool(class, cf.ConstantPool())
	class.fields = newFields(class, cf.Fields())
	class.methods = newMethods(class, cf.Methods())
	return class
}

func getSourceFile(cf *classfile.ClassFile) string {
	if sfAttr := cf.SourceFileAttribute(); sfAttr != nil {
		return sfAttr.FileName()
	}
	return "Unknown"
}

func (k *Class) IsPublic() bool {
	return 0 != k.accessFlags&ACC_PUBLIC
}
func (k *Class) IsFinal() bool {
	return 0 != k.accessFlags&ACC_FINAL
}
func (k *Class) IsSuper() bool {
	return 0 != k.accessFlags&ACC_SUPER
}
func (k *Class) IsInterface() bool {
	return 0 != k.accessFlags&ACC_INTERFACE
}
func (k *Class) IsAbstract() bool {
	return 0 != k.accessFlags&ACC_ABSTRACT
}
func (k *Class) IsSynthetic() bool {
	return 0 != k.accessFlags&ACC_SYNTHETIC
}
func (k *Class) IsAnnotation() bool {
	return 0 != k.accessFlags&ACC_ANNOTATION
}
func (k *Class) IsEnum() bool {
	return 0 != k.accessFlags&ACC_ENUM
}

func (k *Class) isJlObject() bool {
	return k.name == "java/lang/Object"
}
func (k *Class) isJlCloneable() bool {
	return k.name == "java/lang/Cloneable"
}
func (k *Class) isJioSerializable() bool {
	return k.name == "java/io/Serializable"
}

func (k *Class) StaticVars() Slots {
	return k.staticVars
}

func (k *Class) ConstantPool() *ConstantPool {
	return k.constantPool
}

func (k *Class) SourceFile() string {
	return k.sourceFile
}

func (k *Class) isAccessibleTo(other *Class) bool {
	return k.IsPublic() || k.getPackageName() == other.getPackageName()
}

func (k *Class) getPackageName() string {
	if i := strings.LastIndex(k.name, "/"); i >= 0 {
		return k.name[:i]
	}
	return ""
}

func (k *Class) NewObject() *Object {
	return newObject(k)
}

func (k *Class) GetMainMethod() *Method {
	return k.getStaticMethod("main", "([Ljava/lang/String;)V")
}

func (k *Class) getStaticMethod(name string, descriptor string) *Method {
	for _, method := range k.methods {
		if method.IsStatic() && method.name == name && method.descriptor == descriptor {
			return method
		}
	}
	return nil
}

func (k *Class) IsSuperClassOf(other *Class) bool {
	return other.IsSubClassOf(k)
}

func (k *Class) GetPackageName() string {
	if i := strings.LastIndex(k.name, "/"); i >= 0 {
		return k.name[:i]
	}
	return ""
}

func (k *Class) SuperClass() *Class {
	return k.superClass
}

func (k *Class) Name() string {
	return k.name
}

func (k *Class) JavaName() string {
	return strings.Replace(k.name, "/", ".", -1)
}

func (k *Class) InitStarted() bool {
	return k.initStarted
}
func (k *Class) StartedInit() {
	k.initStarted = true
}
func (k *Class) Loader() *ClassLoader {
	return k.loader
}
func (k *Class) JClass() *Object {
	return k.jClass
}

func (k *Class) GetClinitMethod() *Method {
	return k.getStaticMethod("<clinit>", "()V")
}

func (k *Class) getField(name string, descriptor string, isStatic bool) *Field {
	for c := k; c != nil; c = c.superClass {
		for _, field := range c.fields {
			if field.IsStatic() == isStatic &&
				field.name == name && field.descriptor == descriptor {
				return field
			}
		}
	}
	return nil
}
func (k *Class) getMethod(name, descriptor string, isStatic bool) *Method {
	for c := k; c != nil; c = c.superClass {
		for _, method := range c.methods {
			if method.IsStatic() == isStatic &&
				method.name == name &&
				method.descriptor == descriptor {

				return method
			}
		}
	}
	return nil
}

func (k *Class) IsPrimitive() bool {
	_, ok := primitiveTypes[k.name]
	return ok
}

func (k *Class) GetRefVar(fieldName, fieldDescriptor string) *Object {
	field := k.getField(fieldName, fieldDescriptor, true)
	return k.staticVars.GetRef(field.slotId)
}
func (k *Class) SetRefVar(fieldName, fieldDescriptor string, ref *Object) {
	field := k.getField(fieldName, fieldDescriptor, true)
	k.staticVars.SetRef(field.slotId, ref)
}

func (k *Class) GetInstanceMethod(name, descriptor string) *Method {
	return k.getMethod(name, descriptor, false)
}

func (k *Class) GetStaticMethod(name, descriptor string) *Method {
	return k.getMethod(name, descriptor, true)
}

func (k *Class) AccessFlags() uint16 {
	return k.accessFlags
}

func (k *Class) GetFields(publicOnly bool) []*Field {
	if publicOnly {
		publicFields := make([]*Field, 0, len(k.fields))
		for _, field := range k.fields {
			if field.IsPublic() {
				publicFields = append(publicFields, field)
			}
		}
		return publicFields
	} else {
		return k.fields
	}
}

func (self *Class) GetConstructor(descriptor string) *Method {
	return self.GetInstanceMethod("<init>", descriptor)
}

func (self *Class) GetConstructors(publicOnly bool) []*Method {
	constructors := make([]*Method, 0, len(self.methods))
	for _, method := range self.methods {
		if method.isConstructor() {
			if !publicOnly || method.IsPublic() {
				constructors = append(constructors, method)
			}
		}
	}
	return constructors
}
